package com.example.lottery_system.service.impl;

import com.example.lottery_system.common.errorcode.ServiceConstants;
import com.example.lottery_system.common.exception.ServiceException;
import com.example.lottery_system.common.utils.JacksonUtil;
import com.example.lottery_system.common.utils.RedisUtil;
import com.example.lottery_system.contoller.param.CreateActivityParam;
import com.example.lottery_system.contoller.param.CreatePrizeByActivityParam;
import com.example.lottery_system.contoller.param.CreateUserByActivityParam;
import com.example.lottery_system.contoller.param.PageParam;
import com.example.lottery_system.dao.dataobject.ActivityPrizeDO;
import com.example.lottery_system.dao.dataobject.ActivityUserDO;
import com.example.lottery_system.dao.dataobject.PrizeDO;
import com.example.lottery_system.dao.mapper.*;
import com.example.lottery_system.dao.dataobject.ActivityDO;
import com.example.lottery_system.service.ActivityService;
import com.example.lottery_system.service.dto.ActivityDTO;
import com.example.lottery_system.service.dto.ActivityDetailDTO;
import com.example.lottery_system.service.dto.CreateActivityDTO;
import com.example.lottery_system.service.dto.PageListDTO;
import com.example.lottery_system.service.enums.ActivityPrizeStatus;
import com.example.lottery_system.service.enums.ActivityPrizeTiers;
import com.example.lottery_system.service.enums.ActivityStatus;
import com.example.lottery_system.service.enums.ActivityUserStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {

    private final static Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;


    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;


    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId) {
            logger.warn("查询活动详细信息失败，activityId为空");
            return null;
        }

        // 查询 redis
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if(null != detailDTO) {
            logger.info("查询活动详细信息成功！detailDTO={}", JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }

        // redis不存在 查表
        ActivityDO activityDO = activityMapper.selectById(activityId);

        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);

        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);

        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByPrizeIds(prizeIds);

        // 整合活动详细信息，存放redis
        detailDTO = convertToActivityDetailDTO(activityDO, prizeDOList, activityPrizeDOList, activityUserDOList);
        cacheActivity(detailDTO);

        // 返回
        return detailDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class) // 涉及多表
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        // 校验活动信息是否正确
        checkActivityInfo(param);

        // 保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatus.RUNNING.name());
        activityMapper.insert(activityDO);

        // 保存活动关联奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream().map(prizeParam -> {
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
            activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
            activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeStatus.INIT.name());
            return activityPrizeDO;
        }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        // 保存活动关联人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream().map(userParam -> {
            ActivityUserDO activityUserDO = new ActivityUserDO();
            activityUserDO.setActivityId(activityDO.getId());
            activityUserDO.setUserId(userParam.getUserId());
            activityUserDO.setUserName(userParam.getUserName());
            activityUserDO.setStatus(ActivityUserStatus.INIT.name());
            return activityUserDO;
        }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        // 整合完整活动信息，存放在redis（活动+奖品+人员）
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByPrizeIds(prizeIds);

        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO, prizeDOList, activityPrizeDOList, activityUserDOList);
        cacheActivity(detailDTO);

        // 构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }


    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        // 获取总量
        int total = activityMapper.count();

        // 获取当前列表页
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(), param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDTO.getDescription());
                    activityDTO.setStatus(ActivityStatus.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());

        return new PageListDTO<>(total, activityDTOList);
    }




    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId) {
            logger.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        // 查询表数据:活动表、关联奖品表、关联人员表、奖品表
        // redis不存在 查表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if(null == activityDO) {
            logger.error("要缓存的活动id有误！");
            throw new ServiceException(ServiceConstants.CACHE_ACTIVITY_ID_IS_ERROR);
        }

        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);

        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);

        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByPrizeIds(prizeIds);

        // 整合活动详细信息，存放redis
        cacheActivity(
                convertToActivityDetailDTO(
                        activityDO,
                        prizeDOList,
                        activityPrizeDOList,
                        activityUserDOList
                )
        );
    }

    /**
     * 缓存完整的活动信息 ActivityDetail
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        // key:ACTIVITY_activityId
        // value:ActivityDetailDTO(json)
        if(null == detailDTO || null == detailDTO.getActivityId()) {
            logger.warn("要缓存的活动信息不存在！");
            return;
        }

        try {
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        } catch (Exception e) {
            logger.error("缓存活动异常：ActivityDetailDTO={}", JacksonUtil.writeValueAsString(detailDTO), e);
        }

    }

    /**
     * 根据活动id从缓存中获取详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(null == activityId) {
            logger.warn("获取缓存活动数据的activityId为空！");
            return null;
        }

        try {
            String str = redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(StringUtils.hasText(str)) {
                logger.info("获取缓存活动数据为空！key={}", ACTIVITY_PREFIX+activityId);
                return null;
            }
            ActivityDetailDTO detailDTO = JacksonUtil.readValue(str, ActivityDetailDTO.class);
            return detailDTO;
        } catch (Exception e) {
            logger.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX+activityId, e);
            return null;
        }
    }


    /**
     * 根据基本DO整合完整的活动信息
     * @param activityDO
     * @param prizeDOList
     * @param activityPrizeDOList
     * @param activityUserDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        // 活动
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDescription(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatus.forName(activityDO.getStatus()));

        // 奖品
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList.stream()
                .map(activityPrizeDO -> {
                    // activityPrizeDO:{prizeId、prizeAmount...},{prizeId、prizeAmount...}
                    // prizeDO:{id,name,price...},{id,name,price...},{id,name,price...}
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setTiers(ActivityPrizeTiers.forName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setStatus(ActivityPrizeStatus.forName(activityPrizeDO.getStatus()));

                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                                                           .filter(prizeDO -> (prizeDO.getId().equals(activityPrizeDO.getPrizeId())))
                                                           .findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    return prizeDTO;
        }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        // 人员
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                .map(activityUserDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(activityUserDO.getUserId());
                    userDTO.setUserName(activityUserDO.getUserName());
                    userDTO.setStatus(ActivityUserStatus.forName(activityUserDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);

        return detailDTO;
    }


    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(null == param) {
            throw new ServiceException(ServiceConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        // 人员id，是否存在于人员表
        List<Long> userIds = param.getActivityUserList().stream().map(CreateUserByActivityParam::getUserId)
                                                                 .distinct().collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectExistByUserIds(userIds);
        if(CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id->{
            if(!existUserIds.contains(id)) {
                throw new ServiceException(ServiceConstants.ACTIVITY_USER_ERROR);
            }
        });
        // 奖品id，是否存在于奖品表
        List<Long> prizeIds = param.getActivityPrizeList().stream().map(CreatePrizeByActivityParam::getPrizeId)
                                                                   .distinct().collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByPrizeIds(prizeIds);
        if(CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id->{
            if(!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        // 人员数量>=奖品数量
        Integer userAmount = param.getActivityUserList().size();
        Long prizeAmount = param.getActivityPrizeList().stream()
                                                          .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                                                          .sum();
        if(userAmount < prizeAmount) {
            throw new ServiceException(ServiceConstants.PRIZE_USER_AMOUNT_ERROR);
        }

        // 校验活动奖品有效性
        param.getActivityPrizeList().forEach(prize -> {
            if(null == ActivityPrizeTiers.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }

}
